{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "d7957976",
   "metadata": {},
   "source": [
    "# Global CPU limiter design\n",
    "\n",
    "This notebook takes the data produced by the tests and displays it, aiming to provide insight into the behaviour of the algorithm. For one, we can gain confidence that the algorithm works as expected. We might also use this analysis to inform the parameter choice in production."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7ded54da",
   "metadata": {},
   "source": [
    "### Setup\n",
    "\n",
    "Prior to running any of the analysis, we need to do some setup work."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "72b3a572",
   "metadata": {},
   "outputs": [],
   "source": [
    "!pip install pandas matplotlib"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5030a9f4",
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import matplotlib\n",
    "import matplotlib.pyplot as plt\n",
    "from IPython.display import display, HTML\n",
    "\n",
    "matplotlib.rcParams['figure.figsize'] = (20, 10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "04959253",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%javascript\n",
    "IPython.OutputArea.auto_scroll_threshold = 9999"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e5514399",
   "metadata": {},
   "outputs": [],
   "source": [
    "def details(title, objs):\n",
    "    display(HTML(\"<details><summary><h1>%s</h1></summary>%s</details>\" % (title, [ o._repr_html_() for o in objs ])))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6127ae28",
   "metadata": {},
   "source": [
    "### Analysis\n",
    "The function below is our primary driver: it loads a CSV file produced by the tests and generates plots which aim to provide insight into the behaviour."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ef9e7126",
   "metadata": {},
   "outputs": [],
   "source": [
    "def analyse(fn):\n",
    "    display(HTML(\"<h1>\"+fn+\"</h1>\"))\n",
    "    df = pd.read_csv(fn)\n",
    "    df[\"t\"] = df[\"t\"] / (1000.0*1000.0*1000.0)\n",
    "    df[\"dt\"] = df['t'] - df.groupby(\"id\")[\"t\"].shift(1)\n",
    "    df['usage_lag'] = df.groupby(\"id\")[\"usage\"].shift(1)\n",
    "    df['usage_dt'] = df['usage'] - df['usage_lag']\n",
    "    df['usage_fair'] = (df['bwavail'] * df[\"t\"]) / len(df.groupby(\"id\"))\n",
    "    df['usage_avail'] = (df['bwavail'] * df[\"t\"])\n",
    "    df['usage_total'] = [ df['usage'][df['t'] == t].sum() for t in df['t'] ]\n",
    "    df['desiredrate_diff'] = df['actualrate']-df['desiredrate']\n",
    "    \n",
    "    display(HTML(\"<h3>Per Client</h3>\"))\n",
    "    cs = pd.DataFrame()\n",
    "    cs[\"desiredrate_diff\"] = df.groupby(\"id\")['desiredrate_diff'].mean()\n",
    "    cs[\"total_usage\"] = df.groupby(\"id\")['usage'].sum()\n",
    "    cs[\"limit_lo\"] = df.groupby(\"id\")['limit'].min()\n",
    "    cs[\"limit_hi\"] = df.groupby(\"id\")['limit'].max()\n",
    "    cs.columns = ['Desired Date Diff', 'Total Usage', 'Min Limit', 'Max Limit']\n",
    "    display(cs)\n",
    "\n",
    "    display(HTML(\"<h3>Bandwidth</h3>\"))\n",
    "    df[\"bwreq\"].plot();\n",
    "    df[\"bwused\"].plot();\n",
    "    df[\"bwavail\"].plot();\n",
    "    df[\"bwbreak\"].plot();\n",
    "    plt.legend();\n",
    "    plt.title(fn+\": bandwidth use\");\n",
    "    plt.show();\n",
    "    \n",
    "    display(HTML(\"<h3>Usage</h3>\"))\n",
    "    plt.figure();\n",
    "    df.groupby(\"id\")['usage'].plot();\n",
    "    df['usage_fair'].plot();\n",
    "    plt.legend();\n",
    "    plt.title(fn+\": usage\");    \n",
    "    plt.show();\n",
    "    \n",
    "    plt.figure();\n",
    "    df['usage_avail'].plot();\n",
    "    df['usage_total'].plot();\n",
    "    plt.legend();\n",
    "    plt.title(fn+\": total usage\");\n",
    "    plt.show();\n",
    "    \n",
    "    pd.pivot_table(df.reset_index(),\n",
    "               index='t', columns='id', values='usage_dt'\n",
    "              ).plot(subplots=True, title=fn+\": cycle usage\", figsize=(20, 5*len(df.groupby(\"id\"))));\n",
    "    plt.show();\n",
    "\n",
    "    pd.pivot_table(df.reset_index(),\n",
    "               index='t', columns='id', values='usage'\n",
    "              ).plot(subplots=True, title=fn+\": total usage\", figsize=(20, 5*len(df.groupby(\"id\"))));\n",
    "    plt.show();\n",
    "\n",
    "    display(HTML(\"<h3>Rates / Consumption</h3>\"))\n",
    "    pd.pivot_table(df.reset_index(),\n",
    "               index='t', columns='id', values='desiredrate'\n",
    "              ).plot(subplots=True, title=fn+\": desired rate\", figsize=(20, 5*len(df.groupby(\"id\"))));\n",
    "    plt.show();\n",
    "\n",
    "    pd.pivot_table(df.reset_index(),\n",
    "               index='t', columns='id', values='desiredrate_diff'\n",
    "              ).plot(subplots=True, title=fn+\": desired rate diff\", figsize=(20, 5*len(df.groupby(\"id\"))));\n",
    "    plt.show();\n",
    "    \n",
    "    pd.pivot_table(df.reset_index(),\n",
    "               index='t', columns='id', values='limit'\n",
    "              ).plot(subplots=True, title=fn+\": limit\", figsize=(20, 5*len(df.groupby(\"id\"))));\n",
    "    plt.show();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a993253d",
   "metadata": {},
   "source": [
    "# Test Cases\n",
    "\n",
    "The cell below will run all tests and run the analyse function on their result."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9b8cdb3f",
   "metadata": {},
   "outputs": [],
   "source": [
    "!go test -v ."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4b076480",
   "metadata": {},
   "outputs": [],
   "source": [
    "from os import listdir\n",
    "from os.path import isfile, join\n",
    "onlyfiles = [f for f in listdir(\".\") if isfile(join(\".\", f)) and f.startswith(\"sim_\") and f.endswith(\".csv\")]\n",
    "\n",
    "for f in onlyfiles:\n",
    "    analyse(f)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
